package com.jonas.jgraph.inter;

import android.content.Context;
import android.graphics.PointF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.Scroller;

import com.jonas.jgraph.BuildConfig;

/**
 * View 的基础类 处理各种手势的基本
 *
 * @author xujiang
 * @version 2017-08-02 15:13
 */
public class BaseView extends View implements GestureDetector.OnGestureListener {
    private static final String TAG = BaseView.class.getSimpleName();

    public PointF tup;   //点击点的位置
    public PointF longtup;  // 长按点的位置
    public float upPlace;  // 惯性开始的位置

    public Scroller mScroller;  // 滑动类
    public float vSliding;  // 惯性滚动距离

    public GestureDetector mGestureDetector;  // 手势处理类

    public int mHeight;   //控件高减去上下padding
    public int mWidth;   //控件宽减去左右padding
    public PointF mCenterPoint;  // 整个控件（除去padding的中心点）

    //以每秒像素的速度来启动一个fling的最小速度
    public int mMinimumVelocity;
    //最大速度来发起一场投掷，以每秒像素为单位
    public int mMaximumVelocity;

    //  系统认为发生滑动的最小距离
    public int mTouchSlop;

    /**
     * 内部 滑动 距离
     */
    protected float mSliding = 0;  // 为负数 滑动值累积

    public BaseView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();  //系统认为发生滑动的最小距离

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //onTouchEvent( MotionEvent event)中拦截：
        return mGestureDetector.onTouchEvent(event);  //交给手势检测类处理，会在此类中重写方法
    }

    /**
     * onSizeChanged 当view的大小发生变化时触发
     * setBackgroundResource(R.drawable.tabswitcher_long);  可触发
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mHeight = h - getPaddingBottom() - getPaddingTop();
        mWidth = w - getPaddingLeft() - getPaddingRight();
        mCenterPoint = new PointF(w / 2f, h / 2f);  // 控件中点
    }

    /**
     * onFinishInflate 当View中所有的子控件均被映射成xml后触发
     * 当你在使用的时候，可以这样使用
     * MyView mv = (MyView)View.inflate (context,R.layout.my_view,null);
     * 当加载完成xml后，就会执行这个方法。
     */

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        //用户手势检测类，
        mGestureDetector = new GestureDetector(getContext(), this);
        mScroller = new Scroller(getContext());
        ViewConfiguration viewConfiguration = ViewConfiguration.get(getContext());
        mMinimumVelocity = viewConfiguration.getScaledMinimumFlingVelocity();  //以每秒像素的速度来启动一个fling的最小速度
        mMaximumVelocity = viewConfiguration.getScaledMaximumFlingVelocity();  //最大速度来发起一场投掷，以每秒像素为单位
    }

    /**
     *     触发顺序：
     *     onDown->onShowPress->onLongPress
     *   触发顺序：
     点击一下非常快的（不滑动）Touchup：
     onDown->onSingleTapUp->onSingleTapConfirmed
     点击一下稍微慢点的（不滑动）Touchup：
     onDown->onShowPress->onSingleTapUp->onSingleTapConfirmed
     *
     */

    /**
     * 用户轻触触摸屏，由1个MotionEvent ACTION_DOWN触发
     */
    @Override
    public boolean onDown(MotionEvent e) {
        mScroller.forceFinished(true);  //让所有正在进行的动画都恢复原状，也就是停用所有动画
        return true;
    }

    /**
     * 用户轻触触摸屏，尚未松开或拖动，由一个1个MotionEvent ACTION_DOWN触发
     * 注意和onDown()的区别，强调的是没有松开或者拖动的状态
     * 如果是按下的时间超过瞬间，而且在按下的时候没有松开或者是拖动的，
     * 那么onShowPress就会执行
     */
    @Override
    public void onShowPress(MotionEvent e) {
        if (BuildConfig.DEBUG) {
            Log.d(TAG, "onShowPress ");
        }
    }

    /**
     * 用户（轻触触摸屏后）松开，由一个1个MotionEvent ACTION_UP触发
     * 从名子也可以看出,一次单独的轻击抬起操作,也就是轻击一下屏幕，立刻抬起来，才会有这个触发，
     * 当然,如果除了Down以外还有其它操作,那就不再算是Single操作了,所以也就不会触发这个事件
     */
    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        tup = new PointF(e.getX(), e.getY());  // 点击的位置
        if (BuildConfig.DEBUG) {
            Log.d(TAG, "Press point ： " + tup.toString());
        }
        onClick();
        invalidate();  // 刷新
        return true;
    }

    /**
     * 点击事件处理
     */
    public void onClick() {

    }

    /**
     * 用户按下触摸屏，并拖动，由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE触发
     * 在屏幕上拖动事件。无论是用手拖动view，或者是以抛的动作滚动，都会多次触发,这个方法       在ACTION_MOVE动作发生时就会触发
     * 滑屏：手指触动屏幕后，稍微滑动后立即松开
     * onDown-----》onScroll----》onScroll----》onScroll----》………----->onFling
     * 拖动
     * onDown------》onScroll----》onScroll------》onFiling
     *
     * 可见，无论是滑屏，还是拖动，影响的只是中间OnScroll触发的数量多少而已，最终都会触发onFling事件！
     * 手指向右划 distanceX为负数 ，向下划 distanceY为户
     */
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        return onScrollHandler(e1, e2, distanceX, distanceY);
    }

    public boolean onScrollHandler(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        return false;
    }

    /**
     * 用户长按触摸屏，由多个MotionEvent ACTION_DOWN触发
     * 长按触摸屏，超过一定时长，就会触发这个事件
     */
    @Override
    public void onLongPress(MotionEvent e) {
        longtup = new PointF(e.getX(), e.getY());
        if (BuildConfig.DEBUG) {
            Log.d(TAG, "long Press point ： " + longtup.toString());
        }
        onLongClick();
        invalidate();
    }

    /**
     * 长按事件
     */
    public void onLongClick() {

    }

    /**
     * 用户按下触摸屏、快速移动后松开，由1个MotionEvent ACTION_DOWN,
     * 多个ACTION_MOVE, 1个ACTION_UP触发
     *
     * @param e1 手势起点的移动事件
     * @param e2 当前手势点的移动事件
     * @param velocityX 每秒x轴方向移动的像素
     * @param velocityY 每秒y轴方向移动的像素
     */
    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        upPlace = e2.getX();  //触发当前onFling的动作事件。
        mScroller
                .fling((int) e2.getX(), (int) e2.getY(), (int) velocityX / 2, 0, Integer.MIN_VALUE, Integer.MAX_VALUE,
                        0, 0);
        return true;
    }

    /**
     * 会在draw()过程调用该 方法
     * computeScroll：主要功能是计算拖动的位移量、更新背景、设置要显示的屏幕(setCurrentScreen(mCurrentScreen);)。
     * 重写computeScroll()的原因
     * 调用startScroll()是不会有滚动效果的，只有在computeScroll()获取滚动情况，做出滚动的响应
     * computeScroll在父控件执行drawChild时，会调用这个方法
     * 调用view.invalidate(),会触发onDraw和computeScroll()。前提是该view被附加在当前窗口上
     * view.postInvalidate(); //是在非UI线程上调用的
     */
    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {  //computeScrollOffset 功效：返回值为boolean，true说明滚动尚未完成，false说明滚动已经完成。
            vSliding = mScroller.getCurrX() - upPlace;
        } else {
            vSliding = -1;
        }
    }

    /**
     * 设置 滑动距离
     */
    public void setSliding(float sliding) {
        mSliding = sliding;
    }
}
